#include <map>

#include <boost/assign.hpp>

#include "../BoardCodec.h"

using namespace snake;

namespace
{

  char CellToChar(ECellContents cell)
  {
    static const std::map<ECellContents, char> map = boost::assign::map_list_of
      (CELL_EMPTY, ' ')
      (CELL_WALL, '-')
      (CELL_SNAKE_BODY, 'o')
      (CELL_SNAKE_HEAD_LEFT, '<')
      (CELL_SNAKE_HEAD_RIGHT, '>')
      (CELL_SNAKE_HEAD_UP, '^')
      (CELL_SNAKE_HEAD_DOWN, 'v')
      (CELL_FOOD, '!')
      (CELL_POO, '?');

    return map.at(cell);
  }

  ECellContents CellFromChar(char ch)
  {
    static const std::map<char, ECellContents> map = boost::assign::map_list_of
      (' ', CELL_EMPTY)
      ('-', CELL_WALL)
      ('|', CELL_WALL)
      ('+', CELL_WALL)
      ('o', CELL_SNAKE_BODY)
      ('<', CELL_SNAKE_HEAD_LEFT)
      ('>', CELL_SNAKE_HEAD_RIGHT)
      ('^', CELL_SNAKE_HEAD_UP)
      ('v', CELL_SNAKE_HEAD_DOWN)
      ('!', CELL_FOOD)
      ('?', CELL_POO);

    return map.at(ch);
  }

}

Board BoardCodec::FromString(const std::string &code) const
{
  if (code.empty())
    throw std::invalid_argument("empty code is not allowed");

  const size_t size = code.size(), width = static_cast<size_t>(sqrt(size));
  if (width * width != size)
    throw std::invalid_argument("code size should be square");

  Board board(width, width);
  std::string::const_iterator it = code.cbegin();
  for (size_t y = 0; y < width; ++y)
  {
    for (size_t x = 0; x < width; ++x)
    {
      board.SetCell(x, y, ::CellFromChar(*it++));
    }
  }

  return board;
}

std::string BoardCodec::ToString(const Board &board) const
{
  std::string code;

  for (size_t y = 0; y < board.GetHeight(); ++y)
  {
    for (size_t x = 0; x < board.GetWidth(); ++x)
    {
      code += ::CellToChar(board.GetCell(x, y));
    }
  }

  return code;
}